﻿using System;
using Fadd.Globalization;

#if TEST
using Xunit;
#endif

namespace Fadd.Validation
{
	/// <summary>
	/// Attribute to specify that a property might only contain the specified letters (does not fail if the string is null or empty)
	/// </summary>
	public class ValidateContainsAttribute : ValidateAttribute
	{
		private readonly string _letters = string.Empty;

		/// <summary>
		/// The language node item name for the attribute, should be in the form 'Parameter {0} has invalid characters, allowed characters are {1}'
		/// </summary>
		public const string Name = "Contains";


		/// <summary>
		/// Initializes a new instance of the <see cref="ValidateContainsAttribute"/> class.
		/// </summary>
		/// <param name="allowedLetters">The allowed letters</param>
		public ValidateContainsAttribute(string allowedLetters)
		{
			Check.NotEmpty(allowedLetters, "allowedLetters");
			_letters = allowedLetters;
		}


		/// <summary>
		/// Validate value
		/// </summary>
		/// <param name="context">Can be used to send a context object to the validation class. Useful if you provide your own validation classes which need to get information from your application. <seealso cref="T:Fadd.Validation.BeforeValidationEventArgs"/></param>
		/// <param name="value">value to validate</param>
		/// <returns>
		/// true if value passed the validation; otherwise false.
		/// </returns>
		/// <exception cref="NotSupportedException">If a value not of <see cref="string"/> type was supplied</exception>
		public override bool Validate(object context, object value)
		{
			if(value == null)
				return true;
			
			string str = value as string;
			if(str == null)
				throw new NotSupportedException(GetType() + " only supports string properties.");
			
			if(string.IsNullOrEmpty(str))
				return true;

			foreach (char c in str)
			{
				if(_letters.IndexOf(c) == -1)
					return false;
			}

			return true;
		}

		/// <summary>
		/// Localize error if validation failed.
		/// </summary>
		/// <param name="fieldName">Localized field name.</param>
		/// <param name="validationLanguage">Language node with all validation translations.</param>
		public override string Format(string fieldName, ILanguageNode validationLanguage)
		{
			return string.Format(validationLanguage[Name], fieldName, _letters);
		}

		/// <summary>
		/// Determines if the validation class support the specified type.
		/// </summary>
		/// <param name="type">Property/Value type.</param>
		/// <returns>true if the type is a string</returns>
		public override bool SupportsType(Type type)
		{
			return type == typeof(string);
		}

#if TEST

		/// <summary>
		/// Tests different cases of validation
		/// </summary>
		[Fact]
		public static void Test()
		{
			ValidateContainsAttribute attribute = new ValidateContainsAttribute("abcDEF123");
			Assert.Throws(typeof(NotSupportedException), () => attribute.Validate(null, DateTime.Now));
			Assert.Throws(typeof(NotSupportedException), () => attribute.Validate(null, 23.2));

			Assert.True(attribute.Validate(null, "abc"));
			Assert.True(attribute.Validate(null, "DEF"));
			Assert.True(attribute.Validate(null, "123"));

			Assert.False(attribute.Validate(null, null));
			Assert.False(attribute.Validate(null, "xyz"));
			Assert.False(attribute.Validate(null, "567"));
			Assert.False(attribute.Validate(null, "ABCdef"));
		}

#endif
	}
}